-- 2022-8-2

---[[模式

    -- 大多数模式匹配库都使用反斜杠作为转义符，然而，这种方式会导致一些不良的后果
    -- 对于Lua的解析器而言，模式仅仅是普通的字符串
    -- 模式与其他的字符一样遵循同样的规则，并不会被特殊对待
    -- 只有模式匹配相关的函数才会把它们当作模式进行解析
    -- 由于反斜杠是Lua语言在的转义符，所以我们应该避免将他传递给任何函数
    -- 模式本身就难以阅读，到处把"\"换成"\\"就更火上浇油了
    
    -- 我们可以使用双括号把模式括起来构成的长字符串来解决这个问题(某些语言在实际中推荐这种办法)
    -- 然而，长字符串的写法对于通常比较短的模式而言往往又显得冗长
    -- 此外，我们还会失去在模式内进行转义的能力(某些模式匹配工具通过再次实现常见的字符串转义来绕过这种限制)

    -- Lua语言的解决方案更加简单:Lua语言中的模式使用百分号作为转义符(C语言中的一些函数采用的也是同样的方式)，如函数printf和函数strftime
    -- 总体上，所有被转义的字母都具有某些特殊含义(例如'%a'匹配所有字母),而所有被转义的非字母则代表其本身(例如'%.'匹配一个点)

    -- 字符分类模式
    -- 所谓字符分类，就是模式中能够与一个特定集合中的任意字符相匹配的一项
    -- 例如 %d匹配的是任意数字，因此可以使用模式'%d%d/%d%d/%d%d%d%d'来匹配dd/mm/yyyy格式的日期
    -- 如下是所有预置的字符分类及其对应的含义
    --      .       任意字符
    --      %a      字母
    --      %c      控制字符
    --      %d      数字
    --      %g      除空格外的可打印字符
    --      %l      小写字母
    --      %p      标点符号
    --      %s      空白字符
    --      %u      大写字母
    --      %w      字母和数字
    --      %x      十六进制数字
    -- 这些类的大写，对应该类的补集，例如%A表示任意非字母的字符
    print((string.gsub("hello, my-friends.","%A","+")))
    -- 在产生输出函数gsub的返回结果是，我们使用了额外的括号来丢弃第二个结果,也就是替换发生的次数

    -- 当在模式中使用时，还有一些被称为魔法字符的字符具有特殊含义
    -- Lua语言的模式所使用的魔法字符包括
    -- ( ) . % + - * ? [ ] ^ $
    -- %% 匹配一个百分号
    -- %? 匹配一个问号
    -- 我们不仅可以用百分号对魔法字符进行转义，还可以将其用于其他所有字母和数字外的字符
    -- 当不确定是否需要转义时，为了保险起见就可以使用转义符

    -- 可以使用字符集来创建自定义的字符分类，只需要在方括号内将单个字符和字符分类组合起来即可
    -- 例如，字符集'[%w_]'匹配所有以下划线结尾的字母和数字 '[01]'匹配二进制数字 '[%[%]]'匹配方括号
    -- 如果想要统计一段文本中元音的数量，可以这样
    local _,nvow = string.gsub("text","[AEIOUaeiou]","")
    -- 还可以在字符集中包含一段字符范围 做法是写出字符范围的第一个字符和最后一个字符并用横线将它们连接在一起
    -- 由于大多数常用的字符范围都被预先定义了，所以这个功能很少被使用
    -- 例如 '%d'相当于'[0-9]' '%x'相当于'[0-9a-fA-F]' 八进制就可以'[0-7]'比'[01234567]'简便
    
    -- 在字符集前加一个^就可以得到这个字符集的补集:模式'[^0-7]'代表所有八进制数字以外的字符，模式'[^\n]'则代表除换行符以外的其他字符
    -- 尽管如此，我们还是要记得对于简单的分类来说可以使用大写形式来获得对应的补集:'%S'显然要比'[^%s]'更简单

    -- 还可以通过描述模式中重复和可选部分的修饰符(modifier,在其他语言中也被译为限定符)来让模式更加有用
    -- Lua语言中的模式提供了4种修饰符:
    --      +   重复一次或多次
    --      *   重复零次或多次
    --      -   重复零次或多次(最小匹配)
    --      ?   可选(出现零次或一次)

    -- 修饰符 + 匹配原始字符分类中的一个或多个字符，它总是获取与模式相匹配的最长序列
    -- 例如
    -- 模式'%a+'代表一个或多个字母(即一个单词)
    print((string.gsub("one, and two; and three", "%a+", "word")))
    -- 模式'%d+'匹配一个或多个数字(一个整数)
    print(string.match("the number 1298 is even","%d+"))

    -- 修饰符 * 类似于修饰符 + ，但是它还接收对应字符分类出现零次的情况
    -- 该修饰符一个典型的用法就是在模式的部分之间匹配可选的空格 
    -- 例如为了匹配像()或( )这样的空括号对，就可以用模式'%(%s*%)' 其中的%s*匹配零个或多个空格(括号在模式中有特殊含义，所以必须转义)
    -- 另一个示例是用模式'[_%a][_%w]*' 标识符是一个由字母或下划线开头，并紧跟零个或多个由下划线，字母或数字组成的序列

    -- 修饰符 - 类似于修饰符 * ，也是用于匹配原始字符分类的零次或多次出现
    -- 不过，与修饰符*总是匹配能匹配的最长序列不同，修饰符-之后匹配最短序列
    -- 例如
    do
        local test = "int x; /* x */  int y; /* y */"
        print(string.gsub(test,"/%*.*%*/",""))
        print(string.gsub(test,"/%*.-%*/",""))
    end

    -- 修饰符 ? 用于匹配一个可选的字符
    -- 例如我们需要找一个整数，这个整数可能包括一个符号 那么就这样 [+-]?
    do
        for v in string.gmatch("hqweh we-qw+12we+7q","[+-]?%d+") do
            print(v)
        end
    end

    -- 与其他系统不同的是，Lua语言中的修饰符只能作用于一个字符模式，而无法作用于一组分类
    -- 例如，我们不能写出匹配一个可选的单词的模式(除非这个单词只由一个字母组成)
    -- 我们可以使用一些高级技巧来绕开这个限制，后面会介绍

    -- 以补字符^开头的模式表示从目标字符串的开头开始匹配
    -- 类似的，以$结尾的模式表示匹配到目标字符串的结尾
    -- 我们可以同时使用这两个标记来限制匹配查找和锚定模式
    -- 例如，检查字符串是否以数字开头
    print(string.find("2w1eq","^%d"))
    -- 例如，检查字符串是否没有多余前缀和后缀
    print(string.find("+12","^[+-]?%d+$"))
    -- ^和$字符只有位于模式开头和结尾时才具有特殊含义，否则，它们仅仅激素与其自身相匹配的普通字符

    -- 模式'%b'匹配成对的字符串，它的写法是'%bxy',其中x和y是任意两个不同的字符，x作为起始字符，y作为结束字符
    -- 例如，模式'%b()'匹配以左括号开始并以对应右括号结束的子串
    do
        local s = "a (enclosed (in) parenthess) line"
        print(string.gsub(s,"%b()",""))
    end
    -- 通常，我们使用 '%b()' '%b[]'等等作为模式，但实际上可以用任意不同的字符作为分隔符
    
    -- 模式'%f[char-set]'代表前置模式
    -- 该模式只有在后一个字符位于char-set内而前一个字符不在时匹配一个空字符串
    do
        local s = "the anthem is the theme"
        print((string.gsub(s,"%f[%w]the%f[%W]","one")))
    end
    -- 模式'%f[%w]'匹配位于一个非字母或数字的字符和一个字母和数字的字符之间的前置
    -- 而模式'%f[%W]'匹配位于一个字母或数字的字符和一个非字母和数字的字符之间的前置
    -- 主要，即使字符集只有一个分类，也必须将其用括号括起来

    -- 前置模式把目标字符串中第一个字符前和最后一个字符后的位置当成空字符(ASCII编码的\0)
    -- 在前例中，第一个"the"在不属于集合'[%w]'的空字符和属于集合'[%w]'的t之间匹配了一个前置
--]]